package com.loe.ktui.custom

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.TypedArray
import android.graphics.PointF
import android.util.AttributeSet
import android.view.*
import android.view.animation.DecelerateInterpolator
import androidx.core.content.res.getBooleanOrThrow
import androidx.core.content.res.getDimensionOrThrow
import com.loe.ktui.KtDirects
import com.loe.ktui.MATCH_PARENT
import com.loe.ktui.R
import com.loe.ktui.UiGroup
import com.loe.ktui.util.KtCornerProvider

open class UiSlideLayout(id: String? = null) : UiGroup(id)
{
    override var width: Any? = MATCH_PARENT

    var isLeftSwipe: Boolean? = null
    var enable: Boolean? = null

    open fun initUI(context: Context, view: KtSlideLayout)
    {
        super.initUI(context, view)

        isAlphaClick?.let { view.isAlphaClick = it }

        isLeftSwipe?.let { view.isLeftSwipe = it }
        enable?.let { view.isSwipeEnable = it }


        // 递归子元素
        childs.forEach()
        {
            it.createView(context, view)
        }
    }

    /** 创建view */
    override fun createView(context: Context, parent: ViewGroup?): KtSlideLayout
    {
        return KtSlideLayout(context).also()
        {
            initUI(context, it)
            addToParent(parent, it)
        }
    }
}

fun Any.ui_SlideLayout(id: String? = null, block: (UiSlideLayout.() -> Unit)? = null): UiSlideLayout
{
    val ui = UiSlideLayout(id)
    block?.let { ui.it() }
    if (this is UiGroup) addChild(ui)
    return ui
}

open class KtSlideLayout : ViewGroup
{
    constructor(context: Context) : super(context)
    {
        initXML()
    }

    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
    {
        initXML(context.obtainStyledAttributes(attrs, R.styleable.KtSlideLayout))
    }

    constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    {
        initXML(context.obtainStyledAttributes(attrs, R.styleable.KtSlideLayout, defStyleAttr, 0))
    }

    private fun initXML(typedArray: TypedArray? = null)
    {
        mScaleTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
        mMaxVelocity = ViewConfiguration.get(context).scaledMaximumFlingVelocity

        typedArray?.let()
        {
            if (it.hasValue(R.styleable.KtSlideLayout_kt_corner))
            {
                val corner = it.getDimensionOrThrow(R.styleable.KtSlideLayout_kt_corner).toInt()
                val cornerDirect =  KtDirects[it.getInt(R.styleable.KtSlideLayout_kt_cornerDirect, 4)]

                outlineProvider = KtCornerProvider(corner, cornerDirect, this)
            }
            if (it.hasValue(R.styleable.KtSlideLayout_kt_isAlphaClick))
            {
                isAlphaClick = it.getBooleanOrThrow(R.styleable.KtSlideLayout_kt_isAlphaClick)
            }

            if (it.hasValue(R.styleable.KtSlideLayout_kt_isLeftSwipe))
            {
                isLeftSwipe = it.getBooleanOrThrow(R.styleable.KtSlideLayout_kt_isLeftSwipe)
            }

            it.recycle()
        }
    }

    /********************************************** 点击 ****************************************/

    var isAlphaClick = false
        set(value)
        {
            field = value
            isClickable = value
        }

    override fun dispatchSetPressed(pressed: Boolean)
    {
        if (isAlphaClick) alpha = if (pressed || !isEnabled) 0.75f else 1f
        super.dispatchSetPressed(pressed)
    }

    override fun setEnabled(enabled: Boolean)
    {
        if (isAlphaClick) alpha = if (enabled) 1f else 0.75f
        super.setEnabled(enabled)
    }

    /****************************************** 原生  ******************************************/

    private var mScaleTouchSlop = 0
    private var mMaxVelocity = 0
    private var mPointerId = 0
    private var mHeight = 0
    private var mRightMenuWidths = 0
    private var mLimit = 0
    private var mContentView: View? = null
    private val mLastP = PointF()
    private var isUnMoved = true
    private val mFirstP = PointF()
    private var isUserSwiped = false
    private var mVelocityTracker: VelocityTracker? = null
    var isSwipeEnable = true
    var isIos = false
        private set
    private var iosInterceptFlag = false
    var isLeftSwipe = true
        internal set
    private var mExpandAnim: ValueAnimator? = null
    private var mCloseAnim: ValueAnimator? = null
    private var isExpand = false

    fun setIos(ios: Boolean): KtSlideLayout
    {
        isIos = ios
        return this
    }

    fun setLeftSwipe(leftSwipe: Boolean): KtSlideLayout
    {
        isLeftSwipe = leftSwipe
        return this
    }

    private fun init()
    {
        mScaleTouchSlop = ViewConfiguration.get(context).scaledTouchSlop
        mMaxVelocity = ViewConfiguration.get(context).scaledMaximumFlingVelocity
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        this.isClickable = true
        mRightMenuWidths = 0
        mHeight = 0
        var contentWidth = 0
        val childCount = this.childCount
        @SuppressLint("WrongConstant") val measureMatchParentChildren = MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY
        var isNeedMeasureChildHeight = false
        for (i in 0 until childCount)
        {
            val childView = getChildAt(i)
            childView.isClickable = true
            if (childView.visibility != GONE)
            {
                measureChild(childView, widthMeasureSpec, heightMeasureSpec)
                val lp = childView.layoutParams as MarginLayoutParams
                mHeight = Math.max(mHeight, childView.measuredHeight)
                if (measureMatchParentChildren && lp.height == -1)
                {
                    isNeedMeasureChildHeight = true
                }
                if (i > 0)
                {
                    mRightMenuWidths += childView.measuredWidth
                } else
                {
                    mContentView = childView
                    contentWidth = childView.measuredWidth
                }
            }
        }
        setMeasuredDimension(this.paddingLeft + this.paddingRight + contentWidth, mHeight + this.paddingTop + this.paddingBottom)
        mLimit = mRightMenuWidths * 4 / 10
        if (isNeedMeasureChildHeight)
        {
            forceUniformHeight(childCount, widthMeasureSpec)
        }
    }

    override fun generateLayoutParams(attrs: AttributeSet): LayoutParams
    {
        return MarginLayoutParams(this.context, attrs)
    }

    private fun forceUniformHeight(count: Int, widthMeasureSpec: Int)
    {
        @SuppressLint("WrongConstant")
        val uniformMeasureSpec = MeasureSpec.makeMeasureSpec(this.measuredHeight, MeasureSpec.EXACTLY)
        for (i in 0 until count)
        {
            val child = getChildAt(i)
            if (child.visibility != GONE)
            {
                val lp = child.layoutParams as MarginLayoutParams
                if (lp.height == -1)
                {
                    val oldWidth = lp.width
                    lp.width = child.measuredWidth
                    measureChildWithMargins(child, widthMeasureSpec, 0, uniformMeasureSpec, 0)
                    lp.width = oldWidth
                }
            }
        }
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int)
    {
        val childCount = this.childCount
        var left = 0 + this.paddingLeft
        var right = 0 + this.paddingLeft
        for (i in 0 until childCount)
        {
            val childView = getChildAt(i)
            if (childView.visibility != GONE)
            {
                if (i == 0)
                {
                    childView.layout(left, this.paddingTop, left + childView.measuredWidth, this.paddingTop + childView.measuredHeight)
                    left += childView.measuredWidth
                } else if (isLeftSwipe)
                {
                    childView.layout(left, this.paddingTop, left + childView.measuredWidth, this.paddingTop + childView.measuredHeight)
                    left += childView.measuredWidth
                } else
                {
                    childView.layout(right - childView.measuredWidth, this.paddingTop, right, this.paddingTop + childView.measuredHeight)
                    right -= childView.measuredWidth
                }
            }
        }
    }

    override fun dispatchTouchEvent(ev: MotionEvent): Boolean
    {
        if (isSwipeEnable)
        {
            acquireVelocityTracker(ev)
            val verTracker = mVelocityTracker
            when (ev.action)
            {
                0 ->
                {
                    isUserSwiped = false
                    isUnMoved = true
                    iosInterceptFlag = false
                    if (isTouching)
                    {
                        return false
                    }
                    isTouching = true
                    mLastP[ev.rawX] = ev.rawY
                    mFirstP[ev.rawX] = ev.rawY
                    if (viewCache != null)
                    {
                        if (viewCache !== this)
                        {
                            viewCache!!.smoothClose()
                            iosInterceptFlag = isIos
                        }
                        this.parent.requestDisallowInterceptTouchEvent(true)
                    }
                    mPointerId = ev.getPointerId(0)
                }
                1, 3 ->
                {
                    if (Math.abs(ev.rawX - mFirstP.x) > mScaleTouchSlop.toFloat())
                    {
                        isUserSwiped = true
                    }
                    if (!iosInterceptFlag)
                    {
                        verTracker!!.computeCurrentVelocity(1000, mMaxVelocity.toFloat())
                        val velocityX = verTracker.getXVelocity(mPointerId)
                        if (Math.abs(velocityX) > 1000.0f)
                        {
                            if (velocityX < -1000.0f)
                            {
                                if (isLeftSwipe)
                                {
                                    smoothExpand()
                                } else
                                {
                                    smoothClose()
                                }
                            } else if (isLeftSwipe)
                            {
                                smoothClose()
                            } else
                            {
                                smoothExpand()
                            }
                        } else if (Math.abs(this.scrollX) > mLimit)
                        {
                            smoothExpand()
                        } else
                        {
                            smoothClose()
                        }
                    }
                    releaseVelocityTracker()
                    isTouching = false
                }
                2 -> if (!iosInterceptFlag)
                {
                    val gap = mLastP.x - ev.rawX
                    if (Math.abs(gap) > 10.0f || Math.abs(this.scrollX) > 10)
                    {
                        this.parent.requestDisallowInterceptTouchEvent(true)
                    }
                    if (Math.abs(gap) > mScaleTouchSlop.toFloat())
                    {
                        isUnMoved = false
                    }
                    scrollBy(gap.toInt(), 0)
                    if (isLeftSwipe)
                    {
                        if (this.scrollX < 0)
                        {
                            scrollTo(0, 0)
                        }
                        if (this.scrollX > mRightMenuWidths)
                        {
                            scrollTo(mRightMenuWidths, 0)
                        }
                    } else
                    {
                        if (this.scrollX < -mRightMenuWidths)
                        {
                            scrollTo(-mRightMenuWidths, 0)
                        }
                        if (this.scrollX > 0)
                        {
                            scrollTo(0, 0)
                        }
                    }
                    mLastP[ev.rawX] = ev.rawY
                }
            }
        }
        return super.dispatchTouchEvent(ev)
    }

    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean
    {
        if (isSwipeEnable)
        {
            when (ev.action)
            {
                1 ->
                {
                    if (isLeftSwipe)
                    {
                        if (this.scrollX > mScaleTouchSlop && ev.x < (this.width - this.scrollX).toFloat())
                        {
                            if (isUnMoved)
                            {
                                smoothClose()
                            }
                            return true
                        }
                    } else if (-this.scrollX > mScaleTouchSlop && ev.x > (-this.scrollX).toFloat())
                    {
                        if (isUnMoved)
                        {
                            smoothClose()
                        }
                        return true
                    }
                    if (isUserSwiped)
                    {
                        return true
                    }
                }
                2 -> if (Math.abs(ev.rawX - mFirstP.x) > mScaleTouchSlop.toFloat())
                {
                    return true
                }
            }
            if (iosInterceptFlag)
            {
                return true
            }
        }
        return super.onInterceptTouchEvent(ev)
    }

    fun smoothExpand()
    {
        viewCache = this
        if (null != mContentView)
        {
            mContentView!!.isLongClickable = false
        }
        cancelAnim()
        mExpandAnim = ValueAnimator.ofInt(this.scrollX, if (isLeftSwipe) mRightMenuWidths else -mRightMenuWidths)
        mExpandAnim?.addUpdateListener { animation -> scrollTo((animation.animatedValue as Int), 0) }
        mExpandAnim?.setInterpolator(DecelerateInterpolator())
        mExpandAnim?.addListener(object : AnimatorListenerAdapter()
        {
            override fun onAnimationEnd(animation: Animator)
            {
                isExpand = true
            }
        })
        mExpandAnim?.setDuration(200L)?.start()
    }

    private fun cancelAnim()
    {
        if (mCloseAnim != null && mCloseAnim!!.isRunning)
        {
            mCloseAnim!!.cancel()
        }
        if (mExpandAnim != null && mExpandAnim!!.isRunning)
        {
            mExpandAnim!!.cancel()
        }
    }

    fun smoothClose()
    {
        viewCache = null
        if (null != mContentView)
        {
            mContentView!!.isLongClickable = true
        }
        cancelAnim()
        mCloseAnim = ValueAnimator.ofInt(this.scrollX, 0)
        mCloseAnim?.addUpdateListener { animation -> scrollTo((animation.animatedValue as Int), 0) }
        mCloseAnim?.setInterpolator(DecelerateInterpolator())
        mCloseAnim?.addListener(object : AnimatorListenerAdapter()
        {
            override fun onAnimationEnd(animation: Animator)
            {
                isExpand = false
            }
        })
        mCloseAnim?.setDuration(200L)?.start()
    }

    private fun acquireVelocityTracker(event: MotionEvent)
    {
        if (null == mVelocityTracker)
        {
            mVelocityTracker = VelocityTracker.obtain()
        }
        mVelocityTracker!!.addMovement(event)
    }

    private fun releaseVelocityTracker()
    {
        if (null != mVelocityTracker)
        {
            mVelocityTracker!!.clear()
            mVelocityTracker!!.recycle()
            mVelocityTracker = null
        }
    }

    override fun onDetachedFromWindow()
    {
        if (this === viewCache)
        {
            viewCache!!.smoothClose()
            viewCache = null
        }
        super.onDetachedFromWindow()
    }

    override fun performLongClick(): Boolean
    {
        return if (Math.abs(this.scrollX) > mScaleTouchSlop) false else super.performLongClick()
    }

    fun quickClose()
    {
        if (this === viewCache)
        {
            cancelAnim()
            viewCache!!.scrollTo(0, 0)
            viewCache = null
        }
    }

    companion object
    {
        var viewCache: KtSlideLayout? = null
            private set
        private var isTouching = false
    }

    init
    {
        init()
    }
}